<template>
  <view class="hi-tab-bar">
    <view class="hi-tab-bar-inner" id="wrapper">
      <template v-if="scrollable">
        <view class="hi-tab-bar-start" v-if="maskStartShown"></view>
        <view class="hi-tab-bar-end" v-if="maskEndShown"></view>
      </template>

      <scroll-view
        scroll-x
        class="hi-tab-bar-scroll-view"
        scroll-with-animation
        :throttle="false"
        :scroll-left="scrollLeft"
        @scroll="$_onScroll"
      >
        <view class="hi-tab-bar-list" :style="{ width: contentW + 'px' }">
          <view
            class="hi-tab-bar-item"
            :data-index="index"
            :class="{
              'is-active': currentName === item.name,
              'is-disabled': !!item.disabled,
            }"
            v-for="(item, index) in items"
            :key="index"
            :id="'id' + index"
            @click="$_onClick(item, index)"
          >
            <slot
              name="item"
              :item="item"
              :items="items"
              :index="index"
              :currentName="currentName"
              >{{ item.label }}</slot
            >
          </view>
          <span
            class="hi-tab-bar-ink"
            :class="{
              'is-disabled': currentTab && currentTab.disabled,
            }"
            v-if="hasInk"
            :style="{
              width: inkWidth + 'px',
              transform: 'translateX(' + inkPos + 'px)',
            }"
          ></span>
        </view>
      </scroll-view>
    </view>
  </view>
</template>

<script>
let _scrollLock = true // false时, 即表示发生偏移, 使用_scrollLeft作为偏移值
let _scrollLeft = -1 // 拖动产生的偏移

export default {
  name: "navTab",
  props: {
    value: {
      type: [String, Number],
      default: "",
    },
    items: {
      type: Array,
      default: () => [],
    },
    hasInk: {
      type: Boolean,
      default: true,
    },
    inkLength: {
      type: Number,
      default: 100,
    },
  },
  data() {
    return {
      currentName: "",
      wrapperW: 0,
      contentW: 0,
      inkWidth: 0,
      inkPos: 0,
      scrollLeft: 0,
      maskStartShown: false,
      maskEndShown: true,
    }
  },

  computed: {
    scrollable() {
      return this.contentW > this.wrapperW
    },
    currentIndex() {
      return this.items.findIndex((item) => {
        return item.name === this.currentName
      })
    },
    currentTab() {
      return this.items[this.currentIndex]
    },
  },

  watch: {
    value: {
      immediate: true,
      handler(val) {
        if (val !== this.currentName) {
          this.currentName = val
        }
      },
    },
    currentIndex() {
      this.$nextTick(function () {
        this.reflow()
      })
    },
  },

  created() {
    if (this.currentName === "" && this.items.length) {
      this.currentName = this.items[0].name
      this.$emit("change", this.items[0], 0, 0)
    }
  },

  async mounted() {
    await this.$_resizeEnterBehavior()
  },

  methods: {
    // mark: private methods
    $_onScroll({ detail }) {
      if (detail.scrollLeft > 0) {
        this.maskStartShown = true
      } else {
        this.maskStartShown = false
      }

      if (detail.scrollLeft + this.wrapperW >= this.contentW - 2) {
        this.maskEndShown = false
      } else {
        this.maskEndShown = true
      }

      if (_scrollLock) {
        _scrollLock = false
        _scrollLeft = -1
      } else {
        _scrollLeft = detail.scrollLeft
      }
    },
    $_onClick(item, index) {
      if (item.disabled) {
        return
      }

      this.currentName = item.name

      this.$emit("update:value", item.name)
      this.$emit("changeTab", item.name) //设置swiper的第几页
      // this.$parent.currentTab = index //设置swiper的第几页
    },
    $_resizeEnterBehavior() {
      this.reflow()

      // if(this.immediate){
      //   this.$emit('change', this.items[this.currentIndex], this.currentIndex))
      // }
    },
    async $_getOffect(selector) {
      const getContentItem = (s) =>
        new Promise((resolve, reject) => {
          uni
            .createSelectorQuery()
            .in(this)
            .select(s)
            .boundingClientRect((data) => resolve(data))
            .exec()
        })
      return getContentItem(selector)
    },

    // MARK: publick methods
    async reflow() {
      if (this.items.length === 0) {
        return
      }

      const wrapperRect = await this.$_getOffect("#wrapper")
      this.wrapperW = wrapperRect.width

      // calc content width
      let contentWidth = 0
      for (let i = 0, len = this.items.length; i < len; i++) {
        const item = await this.$_getOffect(`#id${i}`)
        contentWidth += item.width
      }

      this.contentW = contentWidth

      _scrollLock = _scrollLeft === -1 // 等于-1则表示没有主动拖动
      let scrollLeft = _scrollLock ? this.scrollLeft : _scrollLeft

      const target = await this.$_getOffect(`#id${this.currentIndex}`)
      this.inkWidth = target.width * (this.inkLength / 100)
      this.inkPos =
        scrollLeft + (target.left - 22) + (target.width - this.inkWidth) / 2

      const prevTarget = this.items[this.currentIndex - 1]
      const nextTarget = this.items[this.currentIndex + 1]

      if (!prevTarget) {
        this.scrollLeft = 0
        return
      }

      if (!nextTarget) {
        this.scrollLeft = scrollLeft + (target.right - wrapperRect.right)
        return
      }

      const prevTargetRect = await this.$_getOffect(
        `#id${this.currentIndex - 1}`
      )
      const nextTargetRect = await this.$_getOffect(
        `#id${this.currentIndex + 1}`
      )

      if (prevTargetRect && prevTargetRect.left < wrapperRect.left) {
        this.scrollLeft = scrollLeft + (prevTargetRect.left - 22)
      } else if (nextTargetRect && nextTargetRect.right > wrapperRect.right) {
        this.scrollLeft =
          scrollLeft + (nextTargetRect.right - wrapperRect.right)
      }
    },
  },
}
</script>

<style lang="scss">
/// tabs
$tab-font-size: 28rpx;
$tab-font-weight: 600;
$tab-text-color: #666f83;
$tab-active-color: #2f86f6;
$tab-disabled-color: #c5cad5;
$tab-bg: #ecf0f3;
$tab-height: 100rpx;
$tab-ink-height: 6rpx;
$tab-offset: 40rpx;
$tab-item-gap: 40rpx;

.hi-tab-bar {
  position: relative;

  padding-right: $tab-offset;
  padding-left: $tab-offset;

  background-color: $tab-bg;
}

.hi-tab-bar-inner {
  position: relative;

  width: 100%;
}

.hi-tab-bar-scroll-view {
  display: flex;

  white-space: nowrap;
}

.hi-tab-bar-list {
  display: flex;

  position: relative;

  min-width: 100%;

  justify-content: space-between;
}

.hi-tab-bar-item {
  display: inline-flex;

  position: relative;

  margin: 0 auto;
  box-sizing: border-box;
  padding: 0 $tab-item-gap;
  min-height: $tab-height;

  font-size: $tab-font-size;
  font-weight: $tab-font-weight;

  color: $tab-text-color;

  flex: auto;
  flex-shrink: 0;
  align-items: center;
  justify-content: center;
  &.is-active {
    color: $tab-active-color;
  }
  &.is-disabled {
    color: $tab-disabled-color;
  }
}

.hi-tab-bar-ink {
  display: block;

  position: absolute;
  bottom: 0;
  left: 0;

  border-radius: 32rpx;
  height: $tab-ink-height;

  background-color: $tab-active-color;

  transition: all 300ms;

  &.is-disabled {
    background-color: $tab-disabled-color;
  }
}

.hi-tab-bar-start,
.hi-tab-bar-end {
  overflow: hidden;

  position: absolute;
  top: 0;
  left: 0;

  width: 14rpx;
  height: $tab-height;
  &::after {
    display: block;

    position: absolute;
    left: -14rpx;

    margin-top: calc(0 - #{$tab-height} * 0.1);
    border-radius: 50%;
    width: 14rpx;
    height: calc(#{$tab-height} * 0.8);

    box-shadow: -1px 0 12px rgba(0, 0, 0, 0.2);

    content: "";
  }
}

.hi-tab-bar-end {
  right: 0;
  left: auto;

  transform: rotate(180deg);
}
</style>
